functor X86Asm (S : X86ASM_STRUCTS) : X86ASM =
struct
  open S

  structure T = Types
  structure Id = Id
  
  val tag = false
  fun debugPrint s =
      if tag
      then print s
      else ()
      
  fun printList (l, separator, ToStr) =
      let fun f x = String.concat [x, separator]
      in  String.concat (List.map (f o ToStr) l)
      end
           
  fun printListDot (l, separator, toStr) =
      case l
       of [] => ""
        | x :: xs =>
          String.concat [toStr x, "\\n", printListDot (xs, separator, toStr)]


  structure Cmd =
  struct
    datatype t
      = Add
      | Sub
      | Mul
      | Div
      | And
      | Or
      | Xor
      | Not
      | Mov
      | Cmp
      | Push
      | Pop
      | Jne
      | Jmp
      | Call
      | Ret
      | Setg
      | Setl
      | Setle
      | Sete
      | Setne
      | Setge
      | Prolog
      | Epilog

    fun toString x =
        case x
         of Add => "addl"
          | Sub => "subl"
          | Mul => "imull"
          | Div => "idivl"
          | And => "andl"
          | Or => "orl"
          | Mov => "movl"
          | Cmp => "cmpl"
          | Push => "pushl"
          | Pop => "popl"
          | Jne => "jne"
          | Jmp => "jmp"
          | Call => "call"
          | Ret => "ret"
          | Setg => "setg"
          | Setl => "setl"
          | Setle => "setle"
          | Sete => "sete"
          | Setne => "setne"
          | Setge => "setge"
          | Not => "notl"
          | Xor => "xorl"
          | Prolog => ""
          | Epilog => ""
  end

  structure Register =
  struct
    datatype t
      = eax | ebx | ecx | edx | esi | edi | ebp | esp
        
    fun toString t =
        case t
         of eax => "%eax"
          | ebx => "%ebx"
          | ecx => "%ecx"
          | edx => "%edx"
          | esi => "%esi"
          | edi => "%edi"
          | ebp => "%ebp"
          | esp => "%esp"
  end
  
  structure Location =
  struct
    datatype t
      = InStack of Intl.t
      | InReg of Register.t
      | Global
      
    fun toString t =
        case t
         of InStack i => Intl.toString2 i
          | InReg r => Register.toString r
          | Global => raise Fail "to do"
  end

  structure Operand =
  struct
    datatype t
      = OpVoid
      | IntNum of Intl.t
      | True
      | False
      | Null
      | Reg of {reg: Register.t, ty : Types.t}
      | Rmem of {off: Intl.t, reg: Register.t, ty : Types.t}
      | Label of {id : Id.t, ty : Types.t}

    fun toString t =
        case t
         of OpVoid => ""
          | IntNum i => "$"^(Intl.toString2 i)
          | True => "$1"
          | False => "$0"
          | Null => "$0"
          | Reg {reg, ty} => Register.toString reg
          | Rmem {off: Intl.t, reg: Register.t, ty : Types.t} =>
            String.concat [Intl.toString2 off, "(", Register.toString reg, ")"]
          | Label {id, ty} => Id.toString id
  end

  structure Ins =
  struct
    datatype t
      = I of {cmd : Cmd.t, src : Operand.t, dest : Operand.t}
    
    fun toString t =
        case t
         of I {cmd = Cmd.Setg, src = Operand.Reg {reg = Register.eax, ty}, dest} =>
            let val cmdStr1 = "setg"
                val srcStr1 = "%al"
                val cmdStr2 = "movzbl"
                val srcStr2 = "%eax"
            in  "\t"^cmdStr1^"\t"^srcStr1^"\n"^
                "\t"^cmdStr2^"\t"^srcStr1^", "^srcStr2^"\n"
            end
          | I {cmd = Cmd.Setle, src = Operand.Reg {reg = Register.eax, ty}, dest} =>
            let val cmdStr1 = "setle"
                val srcStr1 = "%al"
                val cmdStr2 = "movzbl"
                val srcStr2 = "%eax"
            in  "\t"^cmdStr1^"\t"^srcStr1^"\n"^
                "\t"^cmdStr2^"\t"^srcStr1^", "^srcStr2^"\n"
            end
          | I {cmd = Cmd.Setl, src = Operand.Reg {reg = Register.eax, ty}, dest} =>
            let val cmdStr1 = "setl"
                val srcStr1 = "%al"
                val cmdStr2 = "movzbl"
                val srcStr2 = "%eax"
            in  "\t"^cmdStr1^"\t"^srcStr1^"\n"^
                "\t"^cmdStr2^"\t"^srcStr1^", "^srcStr2^"\n"
            end
          | I {cmd = Cmd.Sete, src = Operand.Reg {reg = Register.eax, ty}, dest} =>
            let val cmdStr1 = "sete"
                val srcStr1 = "%al"
                val cmdStr2 = "movzbl"
                val srcStr2 = "%eax"
            in  "\t"^cmdStr1^"\t"^srcStr1^"\n"^
                "\t"^cmdStr2^"\t"^srcStr1^", "^srcStr2^"\n"
            end
          | I {cmd = Cmd.Setne, src = Operand.Reg {reg = Register.eax, ty}, dest} =>
            let val cmdStr1 = "setne"
                val srcStr1 = "%al"
                val cmdStr2 = "movzbl"
                val srcStr2 = "%eax"
            in  "\t"^cmdStr1^"\t"^srcStr1^"\n"^
                "\t"^cmdStr2^"\t"^srcStr1^", "^srcStr2^"\n"
            end
          | I {cmd = Cmd.Setge, src = Operand.Reg {reg = Register.eax, ty}, dest} =>
            let val cmdStr1 = "setge"
                val srcStr1 = "%al"
                val cmdStr2 = "movzbl"
                val srcStr2 = "%eax"
            in  "\t"^cmdStr1^"\t"^srcStr1^"\n"^
                "\t"^cmdStr2^"\t"^srcStr1^", "^srcStr2^"\n"
            end
          | I {cmd = Cmd.Prolog, src = Operand.IntNum i, dest} =>
            let val size = Intl.toString2 i
                val sub = if Intl.isZero i
                          then ""
                          else "\tsubl\t$" ^ size ^ ", %esp\n" 
            in  "\tpushl\t%ebp\n"^
                "\tmovl\t%esp, %ebp\n"^
                "\tpushl\t%ebx\n"^
                "\tpushl\t%esi\n"^
                "\tpushl\t%edi\n"^
                sub
            end
          | I {cmd = Cmd.Epilog, src = Operand.IntNum i, dest} =>
            let val size = Intl.toString2 i
                val add = if Intl.isZero i
                          then ""
                          else "\taddl\t$" ^ size ^ ", %esp\n"
            in  add ^
                "\tpopl\t%edi\n"^
                "\tpopl\t%esi\n"^
                "\tpopl\t%ebx\n"^
                "\tmovl\t%ebp, %esp\n"^
                "\tpopl\t%ebp\n"^
                "\tret\n"
            end
          | I {cmd = Cmd.Div, src, dest} =>
            let val srcStr = Operand.toString src
            in  "\tcltd\n"^
                "\tidivl\t" ^ srcStr ^ "\n"
            end
          | I {cmd, src, dest} =>
            let val cmdStr = Cmd.toString cmd
                val srcStr = Operand.toString src
                val destStr = Operand.toString dest
            in  if destStr = ""
                then "\t" ^ cmdStr ^ "\t" ^ srcStr ^ "\n"
                else "\t" ^ cmdStr ^ "\t" ^ srcStr ^ ", " ^ destStr ^ "\n"
            end
  end

  structure Block =
  struct
    datatype t
      = T of {name : Id.t,
              prop : bool,
              ins : Ins.t list,
              successors : Id.t list} 
              (* the first successor is the direct successor,
               * Example:  je l (l1) ====>
               * the successors are [l1, l]
               *)
    fun toString t =
        case t
         of T {name, prop, ins, successors} =>
            let val label = Id.toString name
                val inss = String.concat (List.map Ins.toString ins)
            in  String.concat [label, " : \n", inss]
            end
    fun getIns t =
        case t
         of T {name, prop, ins, successors} => ins
    fun name t = 
        case t
         of T {name, prop, ins, successors} => name
    fun addInsHead (t, head) =
        case t
         of T {name, prop, ins, successors} => 
            T {name = name, prop = prop, ins = head @ ins, successors = successors}
    fun addInsTail (t, tail) =
        case t
         of T {name, prop, ins, successors} => 
            T {name = name, prop = prop, ins = ins @ tail, successors = successors}        
  end  

	
  structure Function =
  struct
    datatype t
      = T of {name : Id.t,
              exit : Id.t,
              rettype : Types.t,
              args : {id : Id.t, ty : Types.t} list,
              locals : {id : Id.t, ty : Types.t} list,
              blocks : Block.t list,
              pre : Operand.t,
              post : Operand.t}
      
    fun toString t = 
        case t
         of T {name, exit, rettype, args, locals, blocks, pre, post} =>
            let val direction = "\t.globl " ^ (Id.toString name) ^ "\n\n" 
                val body = String.concat (List.map Block.toString blocks)
            in  direction ^ body ^ "\n"
            end
  end
  
  structure Structs =
  struct
    datatype t
      = T of {name : Id.t,
              fields : {id : Id.t, ty : Types.t} list}
              
    fun toString x = ""

  end
  
  structure Program =
  struct
    datatype t
      = T of {structs : Structs.t list,
              gvars : {id : Id.t, ty : Types.t} list,
              funcs : Function.t list}
    fun dataToString d = 
        case d
         of [] => ""
          | {id, ty} :: xs =>
            let val data = (Id.toString id) ^ " : \n" ^  (* size? *)
                           "\t.long 0\n"
            in  data ^ (dataToString xs)
            end
    fun toString t = 
        case t
         of T {structs, gvars, funcs} =>
            let val textHead = "\t.text\n"^
                               "\t.align 4\n\n"
                val textBody = String.concat (List.map Function.toString funcs)
                val dataHead = "\n\n\t.data\n"^
                               "\t.align 4\n\n"
                val globalData = dataToString gvars
                val all = textHead ^ textBody ^ dataHead ^ globalData
            in  all
            end 
        
  end
  
end
